/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.yonsei.iwb.smarttv.userpreference;

import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.vocabulary.RDF;
import edu.yonsei.iwb.smarttv.model.SMARTTVModel;
import edu.yonsei.iwb.smarttv.model.measure.ResourceWeights;
import edu.yonsei.iwb.smarttv.model.measure.WeightModel;
import edu.yonsei.iwb.smarttv.util.SMARTTVLog;
import edu.yonsei.iwb.smarttv.util.UtilMethodSet;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 *
 * @author JDK
 */
public class UserPreferenceSearch {

    private SMARTTVModel tvModel;
    private UserPreferenceModel upModel;
    private UserProfileSet userProfileSet;
    private UserProfile presentUser;
    private ExtendedResources extendedResource;
    private double alpha = 0.5;

    public UserPreferenceSearch(SMARTTVModel tvm, UserPreferenceModel upm, ExtendedResources er) {
        
        this.tvModel = tvm;
        this.upModel = upm;
        this.extendedResource = er;
        this.userProfileSet = this.generateUserProfileDocument();
//        this.upmodel = upm.getUserPreferenceModel();
    }
    
    public UserPreferenceSearch(SMARTTVModel tvm, UserPreferenceModel upm, ExtendedResources er, String userProfile) {        
        this.tvModel = tvm;
        this.upModel = upm;
        this.extendedResource = er;
        this.userProfileSet = this.getUserProfileFromFile(userProfile);
//        this.upmodel = upm.getUserPreferenceModel();
    }
    
    /**
     * get the set of user profile from files
     * @param directory
     * @return 
     */
    private UserProfileSet getUserProfileFromFile(String directory){
        UserProfileSet ups = new UserProfileSet();
        File file;
        BufferedReader br;
        Vector<String> filelist = UtilMethodSet.getFileList(directory);
        for (int i = 0; i < filelist.size(); i++) {            
            String filename = filelist.get(i);
            String id = filename.replaceAll(".profile", "");
//            System.out.println(" - " + id);
            UserProfile up = new UserProfile(id);
            file = new File(UserPreferenceModelType.PROFILE_DIR+"/"+filename);
            try {
                br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
                String getLine;
                while ((getLine = br.readLine()) != null) {
//                    if(getLine.startsWith("uri")){
//                        String uri = getLine.substring(4);
//                        up = new UserProfile(uri);
//                        continue;
//                    }                    
//                    System.out.println(getLine);
                    String[] value = getLine.split(",");
                    String uri = value[0];
                    double weight = Double.parseDouble(value[1]);
                    up.addResourceWeight(uri, weight);
                }
                br.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            ups.put(id, up);
        }        
        return ups;
    }
    
    private UserProfileSet generateUserProfileDocument() {
        UserProfileSet ups = new UserProfileSet();
        File file;
        PrintWriter pw;
        System.out.println("generate user profile document ========================= ");
        try{            
            ResIterator ni = this.upModel.getUserPreferenceModel().listResourcesWithProperty(RDF.type, UserPreferenceURI.USER_PROFILE);
            while(ni.hasNext()){
                Resource user = ni.next();
                file = new File(UserPreferenceModelType.PROFILE_DIR+"/"+user.getLocalName()+".profile");
                pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "euc-kr"));
                file.createNewFile();
                
                Hashtable<Resource, Integer> uptable = this.getUserPreferedResourceCounts(user);
                UserProfile newup = new UserProfile(user.getLocalName(), uptable);                
                ups.put(user.getLocalName(), newup);
//                System.out.println(":::: "+user.getLocalName());
                Enumeration<Resource> e = newup.getResourcePreferenceWeight().keys();
                while(e.hasMoreElements()){
                    Resource key = e.nextElement();
                    double count = newup.getResourcePreferenceWeight().get(key);
//                    System.out.println(key.getLocalName() + "=" + count);
                    pw.write(key.getURI()+","+count+"\n");
                }
//                pw.write("uri,"+user.getURI());
                pw.flush();
            }
            
        }catch(Exception e){
            e.printStackTrace();
        }        
        return ups;        
    }
    
    /**
     * initialize user profile
     * @param id user instance uri
     */
    public void initializeUserProfile(String id) {
        
        if(this.presentUser == null){
            
            System.out.println("firstly user accessed : " + id);                        
            this.presentUser = this.userProfileSet.get(id);
            if(this.presentUser != null){
               this.updateWeightModel(); 
            }else{
                System.out.println("matched user is not exist");
            }
        }else{
            if (!id.equals(this.presentUser.getID())) {                
                System.out.println("new user accessed " + id);
                this.presentUser = this.userProfileSet.get(id);
                this.updateWeightModel();
            }
        }        
    }

//    /**
//     * initialize user profile
//     * @param id user instance uri
//     */
//    public void initializeUserProfile(String id) {
//
//        // 사용자가 기존의 사용자와 같은 경우 
//        if (this.userProfileSet.containsKey(id)) {
//            //사용자의 아이디가 현재 사용자와 다를 때: 
//            if (!id.equals(this.presentUser.getID())) {
//                SMARTTVLog.log(SMARTTVLog.DETAIL, "new user accessed / memory already has user data\n");
//                this.presentUser = this.userProfileSet.get(id);
//                // weight model 이 달라져야 함
//                this.updateWeightModel();
//            }
//        } else {
//            //사용자가 기존의 사용자와 다를 경우            
//            SMARTTVLog.log(SMARTTVLog.DETAIL, "new user accessed\n");
//            Resource r = ResourceFactory.createResource(UserPreferenceURI.USER_ID_NAMESPACE + id);
////            Resource r = ResourceFactory.createResource(id);
//            Hashtable<Resource, Integer> uptable = this.getUserPreferedResourceCounts(r);
//            if (uptable.isEmpty()) {
//                //this user does not have event history data. 
//                WeightModel smarttvwm = this.tvModel.getWeightModel();
//                this.upModel.setWeightModel(smarttvwm);
//            } else {
//                
//                UserProfile newup = new UserProfile(id, uptable);
//                this.presentUser = newup;
//                this.userProfileSet.put(id, newup);
//                
//                // weight model 이 달라져야 함
//                this.updateWeightModel();
//            }
//        }
////        this.rp = this.presentUser.getResourcePreferenceWeight();
//    }

    /**
     * ontology model에서 user와 관련된 선호도 정보를 추출하여 저장
     *
     * @param user User Instance URI Resource
     * @return
     */
    private Hashtable<Resource, Integer> getUserPreferedResourceCounts(Resource user) {
        Hashtable<Resource, Integer> resourceCountTable = new Hashtable<Resource, Integer>();
        Vector<Resource> historyMediaSet = new Vector<Resource>();        
        StmtIterator stmtSearchedItor = this.upModel.getUserPreferenceModel().listStatements(user, UserPreferenceURI.HAS_SEARCHED, (RDFNode) null);        
        while (stmtSearchedItor.hasNext()) {
            Statement stmt = stmtSearchedItor.nextStatement();
            Resource searchingEvent = stmt.getObject().asResource();
            StmtIterator selectedMediaItor = this.upModel.getUserPreferenceModel().listStatements(searchingEvent, UserPreferenceURI.USER_SELECTED_MEDIA, (RDFNode) null);
            while (selectedMediaItor.hasNext()) {
                Statement selectedMediastmt = selectedMediaItor.nextStatement();
                Resource selectedMedia = selectedMediastmt.getObject().asResource();
                historyMediaSet.add(selectedMedia);
            }
        }
        
        StmtIterator stmtWatchedItor = this.upModel.getUserPreferenceModel().listStatements(user, UserPreferenceURI.HAS_WATCHED, (RDFNode) null);
        while (stmtWatchedItor.hasNext()) {
            Statement stmt = stmtWatchedItor.nextStatement();
            Resource watchingEvent = stmt.getObject().asResource();
            StmtIterator watchedMediaItor = this.upModel.getUserPreferenceModel().listStatements(watchingEvent, UserPreferenceURI.USER_WATCHED_MEDIA, (RDFNode) null);
            while (watchedMediaItor.hasNext()) {
                Statement watchedMediastmt = watchedMediaItor.nextStatement();
                Resource watchedMedia = watchedMediastmt.getObject().asResource();
                historyMediaSet.add(watchedMedia);
            }
        }        
                
        Vector<Resource> extendedResources;
        for (int i = 0; i < historyMediaSet.size(); i++) {
            
            Resource media = historyMediaSet.get(i);
            extendedResources = this.extendedResource.getUserPreferedResources(media);
            for (int j = 0; j < extendedResources.size(); j++) {
                if(resourceCountTable.containsKey(extendedResources.get(j))){
                    resourceCountTable.put(extendedResources.get(j),(resourceCountTable.get(extendedResources.get(j))+1));
                }else{
                    resourceCountTable.put(extendedResources.get(j), 1);
                }                
            }
        }
        
        Enumeration<Resource> e = resourceCountTable.keys();
//        System.out.println(":::::::::::::::::::::::::::   "+user+"  :::::::::::::::::::::::");
        while(e.hasMoreElements()){
            Resource r = e.nextElement();
//            System.out.println(r + " / " + resourceCountTable.get(r));
        }
               
        return resourceCountTable;
    }

    /**
     * 기존의 weight model을 업데이트
     */
    private void updateWeightModel() {        
        WeightModel smarttvwm = this.tvModel.getWeightModel();        
        ResourceWeights prw = this.presentUser.getResourcePreferenceWeight();        
        ResourceWeights ow = smarttvwm.getOriginalOntologyWeight();
        
        if(!this.presentUser.isLogIn()){
            Enumeration<Resource> owe = ow.keys();
            while (owe.hasMoreElements()) {
                Resource key = owe.nextElement();
                double originalWeight = 1-ow.get(key);
                if (prw.containsKey(key)) {
                    // 사용자 선호 리소스일 경우 가중치 결합하여 WeightModel에 추가                
                    double mergeWeight = prw.get(key) * this.alpha + originalWeight * (1-this.alpha);
                    prw.put(key, (1-mergeWeight));
                }else{
                    // 사용자 선호 리소스가 아닐 경우 기존 가중치 WeightModel에 추가
                    prw.put(key, 1-(originalWeight*(1-this.alpha)));
                }
            }
            this.presentUser.logIn();
        }                       
        
        this.upModel.setWeightModel(prw, smarttvwm.getTripleWeightTable());
    }

    /**
     * 외부에서 업데이트 된 미디어 바탕의 리소스들로 사용자의 선호도 모델 업데이트
     *
     * @param id
     * @param media 업데이트 대상이 되는 미디어
     */
    public void updateUserPreferenceModel(String id, Resource media) {

        if (this.userProfileSet.containsKey(id)) {
            UserProfile up = this.userProfileSet.get(id);
        } else {
            // preference ontology에 업데이트 상황을 반영함
        }

        throw new UnsupportedOperationException("Not supported yet.");
    }
}
